<?php

require_once("listobject.php");
require_once("evalable.php");

class MacroObject extends Evalable {

    var $bindings = null;
    var $code = null;
    var $env = null;

    function MacroObject($bindings, $code, $env) {
        $this->bindings = $bindings;
        $this->code = $code;
        $this->env = $env;
    }

    function lispeval($env, $args = null) {
        $lisper = $env->get('*this*');
        if ($this->env) {
            $lisper->push_e($this->env->e);
        } else {
            $lisper->push_e();
        }
        $i = 0;
        $n = $this->bindings->count();
        while ($i < $n) {
            if ($this->bindings->getitem($i)->n == "&rest") {
                $lisper->lexical_intern($this->bindings->getitem($i + 1)->n, new ListObject(array_slice($args, 1)));
                $i = $i + 1;
            } else {
                $lisper->lexical_intern($this->bindings->getitem($i)->n, $args[$i]);
            }
            $i = $i + 1;
        }
        $mcode = array();
        foreach ($this->code as $mc) {
            $mcode[] = $mc->lispeval($lisper->e);
        }
        $lisper->pop_e();
        $ans = new LogicObject(0);
        foreach ($mcode as $code) {
            $ans = $code->lispeval($lisper->e);
        }
        return $ans;
    }

    function expand($env, $args) {
        $lisper = $env->get('*self*');
        if ($this->env) {
            $lisper->push_e($this->env . e);
        } else {
            $lisper->push_e();
        }
        $i = 0;
        $n = $this->bindings->count();
        while (i < $n) {
            if ($this->bindings[i]->n == "&rest") {
                $lisper->lexical_intern($this->bindings->getitem($i + 1)->n, new ListObject(array_slice($args, 1)));
                $i = $i + 1;
            } else {
                $lisper->lexical_intern($this->bindings->getitem($i)->n, $args[$i]);
            }
            $i = $i + 1;
        }
        $mcode = array();
        foreach ($this->code as $mc) {
            $mcode[] = $mc->lispeval($lisper->e);
        }

        $lisper->pop_e();
        $list_o = new ListObject($mcode);
        return $list_o->cons('begin');
    }

    function repr() {
        if (count($this->code) == 1) {
            return "(macro " . $this->bindings->repr() . " " . $this->code[0]->repr() . ")";
        } else {
            $ls = new ListObject(array_merge(array('begin'), $this->code));
            $ls = $ls->repr();
            return "(macro " . $this->bindings->repr() . " " . $ls . ")";
        }
    }

    function nullp() {
        return new LogicObject(0);
    }

}
